﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using System.Reflection;
using System.Runtime.Serialization;

namespace TestXNA3.Actors
{
    [Serializable]
    public class Scene : Actor
    {
        public StillDesign.PhysX.Scene PhysicsScene = null;

        public Scene()
        {
            StillDesign.PhysX.SceneDescription sceneDesc = new StillDesign.PhysX.SceneDescription();
            sceneDesc.GroundPlaneEnabled = false;
            PhysicsScene = GameStartup.PhysXCore.CreateScene(sceneDesc);
            _saveable = true;
        }

        // Override base actor methods
        protected override void preUpdate(float dt)
        {
            base.preUpdate(dt);

            if (PhysicsScene != null)
            {
                PhysicsScene.Simulate(dt);
                PhysicsScene.FlushStream();
            }
        }

        protected override void postUpdate(float dt)
        {
            base.postUpdate(dt);
            if (PhysicsScene != null)
            {
                PhysicsScene.FetchResults(StillDesign.PhysX.SimulationStatus.RigidBodyFinished, false);
            }
        }

        public override void AttachActor(Actor A)
        {
            base.AttachActor(A);
            A.Scene = this;
        }

        public override void DetachActor(Actor A)
        {
            base.DetachActor(A);
            if (A.Scene == this)
            {
                A.Scene = null;
            }
        }

        public override bool Trace(Vector3 start, Vector3 end, bool traceComponents, ref float T)
        {
            return traceSelfAndChilds(this, start, end, traceComponents, ref T) != null;
        }

        public Actor traceSelfAndChilds(Actor a, Vector3 start, Vector3 end, bool traceComponents, ref float prevMinDist)
        {
            float minDist = 9999999f;
            Actor nearestActor = null;
            if (!(a is Scene) && a.Trace(start, end, traceComponents, ref minDist) && (minDist < prevMinDist))
            {
                prevMinDist = minDist;
                nearestActor = a;
            }
            foreach (Actor c in a.Childs)
            {
                if (c.Trace(start, end, traceComponents, ref minDist) && (minDist < prevMinDist))
                {
                    prevMinDist = minDist;
                    nearestActor = c;
                }
                else minDist = prevMinDist;
            }
            return nearestActor;
        }

        public Actor TraceScene(Vector3 start, Vector3 end, bool traceComponents)
        {
            float lastDist = 999999f;
            return traceSelfAndChilds(this, start, end, traceComponents, ref lastDist);
        }

        public override void Save(System.IO.BinaryWriter writer)
        {
            long actorCount = 0;
            foreach (Actor a in Childs)
            {
                if (a.Saveable)
                {
                    actorCount++;
                }
            }

            writer.Write(actorCount);
            foreach (Actor a in Childs)
            {
                if (a.Saveable)
                {
                    Type actorType = a.GetType();
                    writer.Write(actorType.FullName);
                    a.Save(writer);
                }
            }
        }

        public override void Load(System.IO.BinaryReader reader)
        {
            long actorCount = reader.ReadInt64();
            for (int i = 0; i < actorCount; i++)
            {
                string assemblyQualifiedName = reader.ReadString();
                object o = Assembly.GetExecutingAssembly().CreateInstance(assemblyQualifiedName);
                if (o is Actor)
                {
                    Actor a = o as Actor;
                    AttachActor(a);
                    a.Load(reader);
                }
                else
                {
                    throw new Exception("Wrong object " + o.GetType() + " found in Scene:Load");
                }
            }
        }

        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            base.GetObjectData(info, context);
        }
    }
}
